package com.ns.school.common.utils;

import java.math.BigDecimal;
import java.math.RoundingMode;

public class DecimalUtil {

    public static final int DEF_SCALE_2 = 2;
    public static final int DEF_SCALE_1 = 1;

    /**
     * 提供精确的加法运算-四舍五入
     *
     * @param value1 被加数
     * @param value2 加数
     * @return 两个参数的和
     */
    public static Double add(String value1, String value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        return b1.add(b2).setScale(DEF_SCALE_2, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 提供精确的加法运算-四舍五入
     *
     * @param value1 被加数
     * @param value2 加数
     * @return 两个参数的和
     */
    public static BigDecimal addB(String value1, String value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        return b1.add(b2).setScale(DEF_SCALE_2, RoundingMode.HALF_UP);
    }

    /**
     * 提供精确的加法运算-四舍五入
     *
     * @param value
     * @return 多个参数的和
     */
    public static BigDecimal addB(String... value) {
        if (value == null || value.length == 0) {
            throw new RuntimeException("求和项不能为空");
        }
        BigDecimal r = BigDecimal.ZERO;
        for (String s : value) {
            if (StrUtil.hasText(s)) {
                r = r.add(new BigDecimal(s));
            }
        }
        return r.setScale(DEF_SCALE_2, RoundingMode.HALF_UP);
    }

    /**
     * 提供精确的加法运算-四舍五入
     *
     * @param value
     * @return 多个参数的和
     */
    public static BigDecimal addB(BigDecimal... value) {
        if (value == null || value.length == 0) {
            throw new RuntimeException("求和项不能为空");
        }
        BigDecimal r = BigDecimal.ZERO;
        for (BigDecimal s : value) {
            if (s != null) {
                r = r.add(s);
            }
        }
        return r.setScale(DEF_SCALE_2, RoundingMode.HALF_UP);
    }

    /**
     * 提供精确的加法运算-四舍五入
     *
     * @param value1 被加数
     * @param value2 加数
     * @return 两个参数的和
     */
    public static BigDecimal addB(BigDecimal value1, BigDecimal value2) {
        return value1.add(value2).setScale(DEF_SCALE_2, RoundingMode.HALF_UP);
    }

    /**
     * 提供精确的加法运算-四舍五入
     *
     * @param value1 被加数
     * @param value2 加数
     * @return 两个参数的和
     */
    public static Double add(Number value1, Number value2) {
        return add(value1.toString(), value2.toString());
    }

    /**
     * 提供精确的加法运算-四舍五入
     *
     * @param value1 被加数
     * @param value2 加数
     * @return 两个参数的和
     */
    public static Double add(Double value1, Double value2) {
        BigDecimal b1 = new BigDecimal(value1.toString());
        BigDecimal b2 = new BigDecimal(value2.toString());
        return b1.add(b2).setScale(DEF_SCALE_2, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * decimal相加
     *
     * @param b1
     * @param b2
     * @return
     */
    public static Double add(BigDecimal b1, BigDecimal b2) {
        return b1.add(b2).setScale(DEF_SCALE_2, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 提供精确的加法运算-四舍五入
     *
     * @param value1 被加数
     * @param value2 加数
     * @return 两个参数的和
     */
    public static BigDecimal addB(Double value1, Double value2) {
        BigDecimal b1 = new BigDecimal(value1.toString());
        BigDecimal b2 = new BigDecimal(value2.toString());
        return b1.add(b2).setScale(DEF_SCALE_2, RoundingMode.HALF_UP);
    }

    /**
     * 提供精确的加法运算-四舍五入
     *
     * @param values 需要相加的数集
     * @return 两个参数的和
     */
    public static Double add(Double[] values) {
        if (values == null || values.length == 0) {
            return null;
        }
        if (values.length == 1) {
            return values[0];
        }
        BigDecimal result = BigDecimal.ZERO;
        for (Double value : values) {
            result = result.add(new BigDecimal(value.toString()));
        }
        return result.setScale(DEF_SCALE_2, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 计算数集中的每个数的占比
     *
     * @param values 需要相加的数集
     * @return 两个参数的和
     */
    public static Double[] percent(Double[] values) {
        if (values == null || values.length == 0) {
            return null;
        }
        if (values.length == 1) {
            return new Double[]{1.0};
        }
        Double[] result = new Double[values.length];
        Double total = add(values);
        for (int i = 0; i < values.length; i++) {
            result[i] = values[i] / total;
        }
        return result;
    }

    /**
     * 计算环比(返回数据-默认2位小数,四舍五入,带%)
     *
     * @param value1 本期数据
     * @param value2 往期数据
     * @return 环比
     */
    public static String ratio(String value1, String value2) {
        return ratio(value1, value2, null);
    }

    /**
     * 计算环比(返回数据-默认2位小数,四舍五入,带%)
     *
     * @param cur  本期数据
     * @param last 往期数据
     * @param def  如果往期为0则返回该默认值
     * @return 环比
     */
    public static String ratio(String cur, String last, String def) {
        BigDecimal b1 = new BigDecimal(cur);
        BigDecimal b2 = new BigDecimal(last);
        if (b2.compareTo(BigDecimal.ZERO) == 0) {
            return def;
        }
        BigDecimal v3 = subB(b1, b2);
        return v3.divide(b2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(DEF_SCALE_2, RoundingMode.HALF_UP) + "%";
    }

    /**
     * 计算环比(返回数据-默认2位小数,四舍五入,带%)
     *
     * @param cur  本期数据
     * @param last 往期数据
     * @param def  如果往期为0则返回该默认值
     * @return 环比
     */
    public static String ratio(BigDecimal cur, BigDecimal last, String def) {
        if (last.compareTo(BigDecimal.ZERO) == 0) {
            return def;
        }
        BigDecimal v3 = subB(cur, last);
        return v3.divide(last, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(DEF_SCALE_2, RoundingMode.HALF_UP) + "%";
    }

    /**
     * 计算环比(返回数据-默认2位小数,四舍五入,带%)
     *
     * @param cur  本期数据
     * @param last 往期数据
     * @param def  如果往期为0则返回该默认值
     * @return 环比
     */
    public static String ratio(Number cur, Number last, String def) {
        return ratio(cur.toString(), last.toString(), def);
    }

    /**
     * 提供精确的减法运算。
     *
     * @param value1 被减数
     * @param value2 减数
     * @return 两个参数的差
     */
    public static double sub(String value1, String value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        return b1.subtract(b2).setScale(DEF_SCALE_2, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 提供精确的减法运算。
     *
     * @param value1 被减数
     * @param value  减数
     * @return 两个参数的差
     */
    public static BigDecimal subB(String value1, String... value) {
        BigDecimal b1 = new BigDecimal(value1);
        for (String value2 : value) {
            BigDecimal b2 = new BigDecimal(value2);
            b1 = subB(b1, b2);
        }
        return b1;
    }

    /**
     * 提供精确的减法运算。
     *
     * @param value1 被减数
     * @param value2 减数
     * @return 两个参数的差
     */
    public static BigDecimal subB(BigDecimal value1, BigDecimal value2) {
        return value1.subtract(value2).setScale(DEF_SCALE_2, RoundingMode.HALF_UP);
    }

    /**
     * 提供精确的减法运算。
     *
     * @param value1 被减数
     * @param value2 减数
     * @return 两个参数的差
     */
    public static double sub(Number value1, Number value2) {
        return sub(value1.toString(), value2.toString());
    }

    /**
     * 提供精确的减法运算。
     *
     * @param value1 被减数
     * @param value2 减数
     * @return 两个参数的差
     */
    public static double sub(Double value1, Double value2) {
        BigDecimal b1 = new BigDecimal(value1.toString());
        BigDecimal b2 = new BigDecimal(value2.toString());
        return b1.subtract(b2).setScale(DEF_SCALE_2, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 提供精确的乘法运算。
     *
     * @param value1 被乘数
     * @param value2 乘数
     * @return 两个参数的积
     */
    public static Double mul(String value1, String value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        return b1.multiply(b2).setScale(DEF_SCALE_2, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 提供精确的乘法运算。
     *
     * @param value1 被乘数
     * @param value2 乘数
     * @param divide 除以的数
     * @return 两个参数的积
     */
    public static Double mulAndDivide(String value1, String value2, String divide) {
        BigDecimal b1 = new BigDecimal(value1);
        b1 = b1.divide(new BigDecimal(divide));
        BigDecimal b2 = new BigDecimal(value2);
        return b1.multiply(b2).setScale(DEF_SCALE_2, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 提供精确的乘法运算。
     *
     * @param value1 被乘数
     * @param value2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal mulB(String value1, String value2) {
        BigDecimal b1 = new BigDecimal(value1);
        BigDecimal b2 = new BigDecimal(value2);
        return mulB(b1, b2);
    }

    /**
     * 提供精确的乘法运算。
     *
     * @param value1 被乘数
     * @param value2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal mulB(BigDecimal value1, BigDecimal value2) {
        return value1.multiply(value2).setScale(DEF_SCALE_2, RoundingMode.HALF_UP);
    }

    /**
     * 提供精确的乘法运算。
     *
     * @param value1 被乘数
     * @param value2 乘数
     * @return 两个参数的积
     */
    public static Double mul(Number value1, Number value2) {
        return mul(value1.toString(), value2.toString());
    }

    /**
     * 提供精确的乘法运算。
     *
     * @param value1 被乘数
     * @param value2 乘数
     * @return 两个参数的积
     */
    public static Double mul(Double value1, Double value2) {
        BigDecimal b1 = new BigDecimal(value1.toString());
        BigDecimal b2 = new BigDecimal(value2.toString());
        return b1.multiply(b2).setScale(DEF_SCALE_2, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 提供精确的乘法运算。
     *
     * @param value1 被乘数
     * @param value2 乘数
     * @return 两个参数的积
     */
    public static BigDecimal mulB(Double value1, Double value2) {
        BigDecimal b1 = new BigDecimal(value1.toString());
        BigDecimal b2 = new BigDecimal(value2.toString());
        return b1.multiply(b2).setScale(DEF_SCALE_2, RoundingMode.HALF_UP);
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时， 精确到小数点以后2位，以后的数字四舍五入。
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @return 两个参数的商
     */
    public static Double div(String dividend, String divisor) {
        return div(dividend, divisor, DEF_SCALE_2);
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时， 精确到小数点以后2位，以后的数字四舍五入。
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @return 两个参数的商
     */
    public static Double div(Number dividend, Number divisor) {
        return div(dividend, divisor, DEF_SCALE_2);
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时， 精确到小数点以后2位，以后的数字四舍五入。
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @return 两个参数的商
     */
    public static Double div(Double dividend, Double divisor) {
        return div(dividend, divisor, DEF_SCALE_2);
    }

    /**
     * 提供（相对）精确的除法运算，当发生除不尽的情况时， 精确到小数点以后10位，以后的数字四舍五入。
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @return 两个参数的商
     */
    public static Double divForTen(Double dividend, Double divisor) {
        return div(dividend, divisor, 10);
    }

    /**
     * 提供（相对）精确的除法运算。 当发生除不尽的情况时，由scale参数指定精度，以后的数字四舍五入。
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @param scale    表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static BigDecimal divB(BigDecimal dividend, BigDecimal divisor, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        return dividend.divide(divisor, scale, RoundingMode.HALF_UP);
    }

    /**
     * 提供（相对）精确的除法运算。 当发生除不尽的情况时，由scale参数指定精度，以后的数字四舍五入。
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @param scale    表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static BigDecimal divB(String dividend, String divisor, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(dividend);
        BigDecimal b2 = new BigDecimal(divisor);
        return divB(b1, b2, scale);
    }

    /**
     * 提供（相对）精确的除法运算。 当发生除不尽的情况时，由scale参数指定精度，以后的数字四舍五入。
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @param scale    表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static Double div(String dividend, String divisor, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(dividend);
        BigDecimal b2 = new BigDecimal(divisor);
        return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 提供（相对）精确的除法运算。 当发生除不尽的情况时，由scale参数指定精度，以后的数字四舍五入。
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @param scale    表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static Double div(Number dividend, Number divisor, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        return div(dividend.toString(), divisor.toString(), scale);
    }

    /**
     * 提供（相对）精确的除法运算。 当发生除不尽的情况时，由scale参数指定精度，以后的数字四舍五入。
     *
     * @param dividend 被除数
     * @param divisor  除数
     * @param scale    表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static Double div(Double dividend, Double divisor, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(dividend.toString());
        BigDecimal b2 = new BigDecimal(divisor.toString());
        return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 提供精确的小数位四舍五入处理。
     *
     * @param value 需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static Double round(Double value, int scale) {
        if (value == null) {
            return null;
        }
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        return roundB(value, scale).doubleValue();
    }

    /**
     * 提供精确的小数位四舍五入处理。
     *
     * @param value 需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static BigDecimal roundB(Double value, int scale) {
        if (value == null) {
            return null;
        }
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(value.toString());
        BigDecimal one = new BigDecimal("1");
        return b.divide(one, scale, RoundingMode.HALF_UP);
    }

    /**
     * 对给定的double数据进行四舍五入保留2位小数
     *
     * @param value
     * @return
     */
    public static Double round(Double value) {
        if (value == null) {
            return null;
        }
        return roundB(value).doubleValue();
    }

    /**
     * 对给定的double数据进行四舍五入保留2位小数
     *
     * @param value
     * @return
     */
    public static BigDecimal roundB(Double value) {
        if (value == null) {
            return null;
        }
        return new BigDecimal(value.toString()).setScale(DEF_SCALE_2, RoundingMode.HALF_UP);
    }

    /**
     * 对给定的double数据进行四舍五入保留2位小数
     *
     * @param value
     * @return
     */
    public static Double round(String value) {
        if (StrUtil.isBlank(value)) {
            return null;
        }
        return roundB(value).doubleValue();
    }

    /**
     * 对给定的double数据进行四舍五入保留2位小数
     *
     * @param value
     * @return
     */
    public static BigDecimal roundB(String value) {
        if (StrUtil.isBlank(value)) {
            return null;
        }
        return new BigDecimal(value).setScale(DEF_SCALE_2, RoundingMode.HALF_UP);
    }

    /**
     * 对给定的BigDecimal数据进行四舍五入保留2位小数
     *
     * @param value
     * @return
     */
    public static BigDecimal roundB(BigDecimal value) {
        if (StrUtil.isBlank(value)) {
            return null;
        }
        return value.setScale(DEF_SCALE_2, RoundingMode.HALF_UP);
    }

    /**
     * 对给定的double数据进行四舍五入保留2位小数
     *
     * @param value
     * @return
     */
    public static String roundStrDefaultZero(Double value) {
        if (value == null) {
            return "0";
        }
        return new BigDecimal(value.toString()).setScale(DEF_SCALE_2, RoundingMode.HALF_UP).toString();
    }

    /**
     * 对给定的double数据进行四舍五入保留2位小数
     *
     * @param value
     * @return
     */
    public static double roundDoubleZero(String value) {
        if (StrUtil.isBlank(value)) {
            return 0.0;
        }
        return roundB(value).doubleValue();
    }

    /**
     * 对给定的double数据进行四舍五入保留2位小数
     *
     * @param value
     * @return
     */
    public static String roundStrDefaultZero(String value) {
        if (StrUtil.isBlank(value)) {
            return "0";
        }
        return new BigDecimal(value).setScale(DEF_SCALE_2, RoundingMode.HALF_UP).toString();
    }

    /**
     * 对给定的double数据进行四舍五入保留2位小数
     *
     * @param value
     * @return
     */
    public static BigDecimal getBigDecimal(Double value) {
        if (value == null) {
            return null;
        }
        return new BigDecimal(value.toString()).setScale(DEF_SCALE_2, RoundingMode.HALF_UP);
    }

    /**
     * 对给定的double数据进行四舍五入保留2位小数
     *
     * @param value
     * @return
     */
    public static BigDecimal getBigDecimal(String value) {
        if (StrUtil.isBlank(value)) {
            return null;
        }
        return new BigDecimal(value).setScale(DEF_SCALE_2, RoundingMode.HALF_UP);
    }

    /**
     * 对给定的BigDecimal数据进行四舍五入保留2位小数
     *
     * @param value
     * @return
     */
    public static BigDecimal getBigDecimal(BigDecimal value) {
        if (value == null) {
            return null;
        }
        return value.setScale(DEF_SCALE_2, RoundingMode.HALF_UP);
    }

    /**
     * 对给定的BigDecimal数据进行四舍五入保留1位小数
     *
     * @param value
     * @return
     */
    public static BigDecimal getBigDecimalOne(BigDecimal value) {
        if (value == null) {
            return null;
        }
        return value.setScale(DEF_SCALE_1, RoundingMode.HALF_UP);
    }

    /**
     * 对给定的BigDecimal数据进行四舍五入保留1位小数
     *
     * @param value
     * @return
     */
    public static BigDecimal getBigDecimalOne(String value) {
        if (StrUtil.isBlank(value)) {
            return null;
        }
        return getBigDecimalOne(new BigDecimal(value));
    }

    /**
     * 对给定的String数据进行四舍五入保留1位小数
     *
     * @param value
     * @return
     */
    public static String getBigDecimalOneStr(String value) {
        if (StrUtil.isBlank(value)) {
            return null;
        }
        return getBigDecimalOne(new BigDecimal(value)).toString();
    }

    public static String sciToDouble(Double sci) {
        java.text.NumberFormat nf = java.text.NumberFormat.getInstance();
        nf.setGroupingUsed(false);
        return nf.format(sci);
    }

    /**
     * 去除多余0
     *
     * @param param
     * @return
     */
    public static String removeZero(Double param) {
        BigDecimal value = new BigDecimal(param.toString());
        // 去除多余0
        BigDecimal noZeros = value.stripTrailingZeros();
        // BigDecimal => String
        return noZeros.toPlainString();
    }

    /**
     * 去除多余0
     *
     * @param param
     * @return
     */
    public static String removeZero(BigDecimal param) {
        // 去除多余0
        BigDecimal noZeros = param.stripTrailingZeros();
        // BigDecimal => String
        return noZeros.toPlainString();
    }
}
